#if defined(__APPLE__) || defined(__linux__)
#include "common.h"

#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>

#include <arpa/inet.h>
#include <sys/time.h>
#if defined(__APPLE__)
#include <sys/sockio.h>
#endif
#include <sys/sysctl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <net/if.h>
#if defined(__APPLE__)
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_media.h>
#include "typedef_mac.h"
typedef struct sockaddr SOCKADDR;  
#endif
#include <net/ethernet.h>
#include <ifaddrs.h>
#endif

#include <stdio.h>
#include <string.h>
#include "socket_stream.h"
#include "iris_msg.h"
#include "ObjLog.h"
#include "PerfLog.h"

#ifdef WIN32
#include "zetastmp.h"
#include "Ws2tcpip.h"
#include "netdev_wmi.h"
#endif

#if(ENABLE_CRYPTO)
#include "STCrypto.h"
#endif

#ifdef WIN32 
#define SNPRINTF				_snprintf
#else
#define SNPRINTF				snprintf
#endif

#define DEFAULT_SMALL_PKT_SIZE	1024
#define MALLOC(x)				HeapAlloc(GetProcessHeap(), 0, (x)) 
#define FREE(x)					HeapFree(GetProcessHeap(), 0, (x))

#ifdef __APPLE__
#define	min(a,b)                ((a) < (b) ? (a) : (b))
#define	max(a,b)                ((a) > (b) ? (a) : (b))
#define BUFFERSIZE		4096
#endif

socket_stream::socket_stream(SOCKET sockfd)
{
    m_sockfd	= sockfd; 
    memset(&m_addr, 0, sizeof(m_addr));
/*
#if (ENABLE_CRYPTO) && defined _SRFEATURE
    m_pCryptor = new STCryptor(false);
#else*/
    m_pCryptor = NULL;
//#endif

#if (ENABLE_CRYPTO)
	memset(m_szSocketName, 0, sizeof(m_szSocketName));
#endif
#if (SZETA_ENABLE)
    m_ZetaSockPktSend = new StZetaStamp();
#endif

	m_nSocktype = SOCKET_TYPE_TCP;
	m_mutex.Init();
}

socket_stream::~socket_stream()
{

#if (SZETA_ENABLE)
    m_ZetaSockPktSend->StampSum();
    delete m_ZetaSockPktSend;
	m_ZetaSockPktSend = NULL;
#endif

    close();
/*
#if (ENABLE_CRYPTO) && defined _SRFEATURE
    if ( m_pCryptor )
        delete m_pCryptor;
#endif*/
    m_pCryptor = NULL;

	m_mutex.UnInit();
}

bool
socket_stream::create_server_socket(int port)
{
    int					status;

#ifdef WIN32
    bool				optval	= 1;
#elif defined(__APPLE__) || defined(__linux__)
	int					optval  = 1;
#endif

	m_sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (false == is_valid()) {
		return false;
	}

	memset(&m_addr, 0, sizeof (m_addr));
	m_addr.sin_family = AF_INET;
	m_addr.sin_addr.s_addr = INADDR_ANY;
	m_addr.sin_port = htons(port);

	status = setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
	if (SOCKET_ERROR == status) {
		goto error;
	}

	status = bind(m_sockfd, (struct sockaddr *)&m_addr, sizeof (m_addr));
	if (SOCKET_ERROR == status) {
		goto error;
	}

	status = listen(m_sockfd, 16);
	if (SOCKET_ERROR == status) {
		goto error;
	}
	return true;

error:
	if (m_sockfd != INVALID_SOCKET) {

#ifdef WIN32
		shutdown(m_sockfd, 2);
		closesocket(m_sockfd);
#elif defined(__APPLE__) || defined(__linux__)
		::close(m_sockfd);
#endif

		m_sockfd = INVALID_SOCKET;
	}
	return false;
}

bool
socket_stream::create_client_socket()
{
    int		status;
    int		optval	= 1;
	bool	succ = true;
	DWORD	rwtimeout = 1000 * 15; // timeout 15 secs

    /* for andriod, it protocol use 0, but win32-client use 'IPPROTO_TCP' */
    // m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
	m_sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (false == is_valid()) {
		status = WSAGetLastError();
		return false;
	}

    //status = ::setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(optval));
    status = setsockopt(m_sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(optval));
	if (status == SOCKET_ERROR) {
		succ = false;
	}

	optval = 64*1024;
	status = setsockopt(m_sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&optval, sizeof(optval));
	if (status == SOCKET_ERROR) {
		succ = false;
	}

#ifdef WIN32
	status = setsockopt(m_sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&rwtimeout, sizeof(rwtimeout));
	if (status == SOCKET_ERROR) {
		succ = false;
	}

	status = setsockopt(m_sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&rwtimeout, sizeof(rwtimeout));
	if (status == SOCKET_ERROR) {
		succ = false;
	}
#elif defined(__APPLE__) || defined (__linux__)
	struct timeval tv;
	tv.tv_sec  = rwtimeout/1000;
	tv.tv_usec = 0;

	status = setsockopt(m_sockfd, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
	if (status == SOCKET_ERROR) {
		succ = false;
	}

	status = setsockopt(m_sockfd, SOL_SOCKET, SO_SNDTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
	if (status == SOCKET_ERROR) {
		succ = false;
	}
#endif

    return succ;
}

int
socket_stream::send_packet(const void * pBuf, unsigned int iSize, unsigned int iPktSize)
{
	m_mutex.Lock();

	int				sent = 0;
    unsigned int    spkt = 0;
    unsigned int	remain = 0;
    unsigned char *	p;

#if (SZETA_ENABLE)
    m_ZetaSockPktSend->StampDataEnter(iSize);
#endif

    p = (unsigned char *) pBuf;
    remain = iSize;
    spkt = iPktSize > 0 ? iPktSize : remain;
    /*
     * the while(remain) has some problem, because it need the function
     * to return until finish reading the whole package
     */
    while (remain) {
#ifdef __linux__
        sent = ::send(m_sockfd, (char *)p, remain > spkt ? spkt : remain, MSG_NOSIGNAL);
#else
        sent = ::send(m_sockfd, (char *)p, remain > spkt ? spkt : remain, 0);
#endif
		if (sent == SOCKET_ERROR) {
			m_mutex.UnLock();
			return -1;
		}
		p += sent;
		remain -= sent;
    }

#if (SZETA_ENABLE)
    m_ZetaSockPktSend->StampExit();
#endif

	m_mutex.UnLock();
    return iSize;
}

int
socket_stream::recv_packet(void * pBuf, unsigned int iSize)
{
	int				rcvd;
    unsigned int	remain;
    unsigned char *	p;

    p = (unsigned char *) pBuf;
    remain = iSize;
    /*
     * the while(remain) has some problem, because it need the function
     * to return until finish reading the whole package
     */
    while (remain) {
		rcvd = ::recv(m_sockfd, (char *)p, remain, 0);
		if (rcvd <= 0) {
			return rcvd;
		}

		p += rcvd;
		remain -= rcvd;
    }

    return iSize;
}

SOCKET
socket_stream::accept(int timeout)
{
	SOCKET cli_sd;
	timeval timeTimeout;
	struct sockaddr_in pin;
	int ret;

#ifdef WIN32
	int alen = sizeof(struct sockaddr);
#elif defined(__APPLE__) || defined(__linux__)
	socklen_t alen = sizeof(struct sockaddr);
#endif

	fd_set readfds;
	FD_ZERO(&readfds);
	FD_SET(m_sockfd, &readfds);
	timeTimeout.tv_sec = timeout;
	timeTimeout.tv_usec = 0;

#ifdef WIN32
	ret = ::select(0, &readfds, NULL, NULL, &timeTimeout);
#elif defined(__APPLE__) || defined(__linux__)
	ret = ::select(m_sockfd+1, &readfds, NULL, NULL, &timeTimeout);
#endif

	if (ret <= 0) {
		return INVALID_SOCKET;
	}

	cli_sd = ::accept(m_sockfd, (struct sockaddr *) &(pin), &alen);
	if (cli_sd == -1) {
		return INVALID_SOCKET;
	}

	return cli_sd;
}

bool
socket_stream::connect(const char * host, const USHORT port)
{
    int			status;
    struct hostent *	hp;

    if (false == is_valid()) return false;
    
    hp = gethostbyname(host);
    if (!hp) return false;

    memset(&m_addr, 0, sizeof(m_addr)); 
    m_addr.sin_family		= AF_INET; 
    m_addr.sin_port		= htons(port); 
    m_addr.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr;

	status = ::connect(m_sockfd, (sockaddr*)&m_addr, sizeof(m_addr));
    return (status != SOCKET_ERROR);
}

void
socket_stream::set_block(const bool block)
{
    int	status;
	int	optval = 1;

    // todo: set other options
    status = setsockopt(m_sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(optval));
}

void
socket_stream::set_timeout(DWORD	rwtimeout)
{
    int		status;
	bool	succ = true;

#ifdef WIN32
	status = setsockopt(m_sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&rwtimeout, sizeof(rwtimeout));
	if (status == SOCKET_ERROR) {
		succ = false;
	}

	status = setsockopt(m_sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&rwtimeout, sizeof(rwtimeout));
	if (status == SOCKET_ERROR) {
		succ = false;
	}
#elif defined(__APPLE__) || defined(__linux__)
	struct timeval tv;
	tv.tv_sec  = rwtimeout/1000;
	tv.tv_usec = 0;

	status = setsockopt(m_sockfd, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
	if (status == SOCKET_ERROR) {
		succ = false;
	}
	
	status = setsockopt(m_sockfd, SOL_SOCKET, SO_SNDTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
	if (status == SOCKET_ERROR) {
		succ = false;
	}
#endif
	
}

void
socket_stream::set_connect_block(const bool block)
{
	int				status;
	bool			succ;
	unsigned long	optval;

	if (!is_valid()) {
		return;
	}

	succ	= true;
	if (block) {
		optval	= 0;
	} else {
		optval	= 1;
	}
	
#ifdef WIN32
	status	= ioctlsocket(m_sockfd, FIONBIO, (unsigned long*)&optval);
#elif defined(__APPLE__) || defined(__linux__)
	status	= fcntl(m_sockfd, F_SETFL, O_NONBLOCK);
#endif
	
	if (status == SOCKET_ERROR) {
		succ = false;
	}
}

bool
socket_stream::is_valid() const
{
    return (m_sockfd != INVALID_SOCKET);
}

/***********************************************************************
 * Close socket
 * ::shutdown() accept SHUT_RD SHUT_WR SHUT_RDWR return zero on success
 * Return EBADF ENOTSOCK ENOTCONN on error
 * ::close() may not close socket immediately
 ***********************************************************************/
bool
socket_stream::close()
{
    int		status = 0;
    if (is_valid()) {
		/*
		 * status = ::close(m_sockfd);
		 * // ::close may delay for other references, may not wakeup select()
		 */
#ifdef WIN32
		shutdown(m_sockfd, 2);
		status	= closesocket(m_sockfd);
#elif defined(__APPLE__) || defined(__linux__)
		status = ::close(m_sockfd);
#endif
		m_sockfd	= INVALID_SOCKET;
    }
    return (status != SOCKET_ERROR);
}

int
socket_stream::get_sockfd()
{
    return m_sockfd;
}

int
socket_stream::send(const void *pBuf, unsigned int iSize)
{
#if (ENABLE_CRYPTO)
    return send(pBuf, iSize, false, true, 0);
#else
    return send_packet(pBuf, iSize);
#endif
}

int
socket_stream::recv(void *pBuf, unsigned int iSize)
{
#if (ENABLE_CRYPTO)
    return recv(pBuf, iSize, true);
#else
    return recv_packet(pBuf, iSize);
#endif
}

#ifdef WIN32
PIP_ADAPTER_ADDRESSES
socket_stream::IpAdapterDup(PIP_ADAPTER_ADDRESSES adapter)
{
	PIP_ADAPTER_ADDRESSES dup;

	dup = (PIP_ADAPTER_ADDRESSES)malloc(sizeof(*dup));
	memset(dup, 0, sizeof(*dup));

	if (adapter) {
		memcpy(dup, adapter, sizeof(*adapter));

		// copy Description (need to be free)
		dup->Description = _wcsdup(adapter->Description);
	}

	return dup;
}

void
socket_stream::IpAdapterFree(PIP_ADAPTER_ADDRESSES dup)
{
	if (dup)
	{
		if ( dup->Description )
		{
			free(dup->Description);
		}

		free(dup);
	}
}

int
socket_stream::GetAdapterAddressByIp(char *ip, PIP_ADAPTER_ADDRESSES *adapter)
{
	PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL;
	PIP_ADAPTER_ADDRESSES AdapterList = NULL;
	ULONG OutBufferLength = 0;
	ULONG RetVal = 0, i = 0;
	int matched = 0;

	*adapter = NULL;
#ifndef _USRDLL
	for (i = 0; i < 5; i++) {
		RetVal = GetAdaptersAddresses(
			AF_INET, 
			0,
			NULL,
			AdapterAddresses, 
			&OutBufferLength);

		if (RetVal != ERROR_BUFFER_OVERFLOW)
			break;

		if (AdapterAddresses != NULL)
			FREE(AdapterAddresses);

		AdapterAddresses = (PIP_ADAPTER_ADDRESSES) MALLOC(OutBufferLength);
		if (AdapterAddresses == NULL) {
			RetVal = GetLastError();
			break;
		}
	}

	if (RetVal == NO_ERROR) {
		AdapterList = AdapterAddresses;
		while (AdapterList) {
			struct sockaddr_in * addr_in;

			if (AdapterList->FirstUnicastAddress) {
				PIP_ADAPTER_UNICAST_ADDRESS unicast_addr;
				unicast_addr = AdapterList->FirstUnicastAddress;

				do {
					addr_in = (struct sockaddr_in*)unicast_addr->Address.lpSockaddr;

					// compare IP
					if (strcmp(ip, inet_ntoa(addr_in->sin_addr)) == 0) {
						matched = 1;
						break;
					}

					unicast_addr = unicast_addr->Next;
				} while (unicast_addr);
			}
			if (matched == 1)
				break;
			AdapterList = AdapterList->Next;
		}
	}

	// if we found a matched adapter
	if (AdapterList) {
		OBJ_DBG("found matched adapter");

		int nLen = WideCharToMultiByte(CP_ACP, 0, AdapterList->FriendlyName, -1, NULL, 0, NULL, NULL);
		char *pName = new char[nLen];
		WideCharToMultiByte(CP_ACP, 0, AdapterList->FriendlyName, -1, pName, nLen, NULL, NULL);
		OBJ_DBG("friendly name: %s", pName);
		delete pName;

		//OBJ_DBG("friendly name: %S", AdapterList->FriendlyName);
		OBJ_DBG("description: %S", AdapterList->Description);

		*adapter = IpAdapterDup(AdapterList);
	} else {
		*adapter = IpAdapterDup(NULL);
	}

	if (AdapterAddresses)
		FREE(AdapterAddresses);
#endif
	return matched;
}

int
socket_stream::GetNetDevTypeByAdapterAddress(PIP_ADAPTER_ADDRESSES adapter)
{
	int netdev_type = NETDEV_TYPE_LAN;
#ifndef _USRDLL
	if (adapter) {
		if (adapter->IfType == IF_TYPE_IEEE80211) {
			netdev_type = NETDEV_TYPE_WLAN;
		} else {
			// check network adapter type, only support wireless now
			if (Is80211NetDevWMI(adapter->Description) == TRUE)
				netdev_type = NETDEV_TYPE_WLAN;
		}
	}
#endif
	return netdev_type;
}

int
socket_stream::GetAdapterIpBySourceSocketAddr(char *ip, sockaddr_in * src_addr)
{
	int matched = 0;

	SOCKET s = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0);

	INTERFACE_INFO localAddrs[64];
	DWORD bytesReturned;
	if (WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &localAddrs, sizeof(localAddrs), &bytesReturned, NULL, NULL) == SOCKET_ERROR)
	{
		// TODO:
	}
	closesocket(s);

	int numLocalAddrs = (bytesReturned/sizeof(INTERFACE_INFO));

	for (int i = 0; i < numLocalAddrs; i++) {
		struct sockaddr_in *ip_addr, *netmask;
		DWORD if_subnet, src_subnet;

		ip_addr = (struct sockaddr_in*)&localAddrs[i].iiAddress;
		netmask = (struct sockaddr_in*)&localAddrs[i].iiNetmask;

		if_subnet  = ip_addr->sin_addr.S_un.S_addr & netmask->sin_addr.S_un.S_addr;
		src_subnet = src_addr->sin_addr.S_un.S_addr & netmask->sin_addr.S_un.S_addr;

		if (if_subnet == src_subnet) {
			DWORD dwip = ip_addr->sin_addr.S_un.S_addr;
			sprintf(ip, "%d.%d.%d.%d", dwip & 0xFF, (dwip >> 8) & 0xFF,
				(dwip >> 16) & 0xFF, (dwip >> 24) & 0xFF);
			matched = 1;
			OBJ_DBG("matched AdapterIp: %s", ip);
			break;
		}
	}

	return matched;
}
#elif defined (__APPLE__)
void
socket_stream::IpAdapterFree(PIP_ADAPTER_ADDRESSES dup)
{
	if (dup)
		free(dup);
}

void
socket_stream::GetAdapterAddressByIp(char *ip, PIP_ADAPTER_ADDRESSES adapter)
{
	struct ifconf ifc;
	struct ifreq *ifr;
	int sockfd;
	char buffer[BUFFERSIZE], *cp, *cplim;
	char temp[80];
	char ifr_name[IFNAMSIZ];
    
	memset(adapter, 0, sizeof(IP_ADAPTER_ADDRESSES));
	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket failed");
		return;
	}
	
	ifc.ifc_len = BUFFERSIZE;
	ifc.ifc_buf = buffer;
	
	if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
	{
		perror("ioctl error");
		::closesocket(sockfd);
		return;
	}
	
	ifr = ifc.ifc_req;
	memset(ifr_name, 0, sizeof(ifr_name));
	
	cplim = buffer + ifc.ifc_len;
	
	for (cp=buffer; cp < cplim; )
	{
		ifr = (struct ifreq *)cp;
		
		if (ifr->ifr_addr.sa_family == AF_INET)
		{
			struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
			if (!strcmp(ip, inet_ntop(AF_INET, &sin->sin_addr, temp, sizeof(temp)))) {
				strncpy(ifr_name, ifr->ifr_name, sizeof(ifr_name));
			}
		}
		
		cp += sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
	}
	
	for (cp=buffer; cp < cplim; )
	{
		ifr = (struct ifreq *)cp;
		
		if (ifr->ifr_addr.sa_family == AF_LINK)
		{
			struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
			
			switch(sdl->sdl_type)
			{
				case IFT_ETHER:
					if (strcmp(ifr_name, ifr->ifr_name)) {
						break;
					}
					
					struct ifmediareq ifmr;
					
					memset(&ifmr, 0, sizeof(ifmr));
					strncpy(ifmr.ifm_name, ifr->ifr_name, sizeof(ifmr.ifm_name));
					
					if (ioctl(sockfd, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
						break;
					}
					
					if (ifmr.ifm_status & IFM_AVALID) {
						memcpy(adapter->PhysicalAddress, (const struct ether_addr *)LLADDR(sdl), ETHER_ADDR_LEN);
						adapter->PhysicalAddressLength = ETHER_ADDR_LEN;
						
						switch (IFM_TYPE(ifmr.ifm_active))
						{
							case IFM_ETHER:
								if (ifmr.ifm_status & IFM_ACTIVE) {
									adapter->PhysicalType = NETDEV_TYPE_LAN;
								}
								break;
							case IFM_IEEE80211:
								// XXX: Different value for adhoc?
								if (ifmr.ifm_status & IFM_ACTIVE) {
									adapter->PhysicalType = NETDEV_TYPE_WLAN;
								}
								break;
							default:
								adapter->PhysicalType = NETDEV_TYPE_WLAN;
								break;
						}
					}
					break;
				default:
					break;
			}
		}
		cp += sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
	}
	
	::closesocket(sockfd);
	return;
}

int
socket_stream::GetNetDevTypeByAdapterAddress(PIP_ADAPTER_ADDRESSES adapter)
{
	return (adapter ? adapter->PhysicalType:NETDEV_TYPE_LAN);
}

int
socket_stream::GetAdapterIpBySourceSocketAddr(char *ip, sockaddr_in * src_addr)
{
	int matched = 0;
    
	struct ifaddrs *if_addrs = NULL;
	struct ifaddrs *if_addr = NULL;
	
	if (0 == getifaddrs(&if_addrs)) {
		for (if_addr = if_addrs; if_addr != NULL; if_addr = if_addr->ifa_next) {
			if (!strncmp(if_addr->ifa_name, "lo", 2))
				continue;
            
			if (if_addr->ifa_addr->sa_family != AF_INET)
				continue;
            
			if (if_addr->ifa_netmask == NULL)
				continue;
            
			if (if_addr->ifa_netmask->sa_family != AF_INET)
				continue;
            
			struct sockaddr_in *ip_addr, *netmask;
			DWORD if_subnet, src_subnet;
			
			ip_addr = (struct sockaddr_in*)((struct sockaddr_in *)if_addr->ifa_addr);
			netmask = (struct sockaddr_in*)((struct sockaddr_in *)if_addr->ifa_netmask);
			
			if_subnet  = ip_addr->sin_addr.s_addr & netmask->sin_addr.s_addr;
			src_subnet = src_addr->sin_addr.s_addr & netmask->sin_addr.s_addr;
			
			if (if_subnet == src_subnet) {
				DWORD dwip = ip_addr->sin_addr.s_addr;
				sprintf(ip, "%d.%d.%d.%d", dwip & 0xFF, (dwip >> 8) & 0xFF,
						(dwip >> 16) & 0xFF, (dwip >> 24) & 0xFF);
				matched = 1;
				OBJ_DBG("matched AdapterIp: %s", ip);
				break;
			}
		}
	}
    
	return matched;
}
#endif

void
socket_stream::DumpSocketError(const char *log, int err)
{
	char buf[128];
	int num;

#ifdef WIN32
	int wsErr = WSAGetLastError();
#endif
	num = SNPRINTF(buf, sizeof(buf), "%s: err=%d(0x%X)", log, err, err);

#ifdef WIN32
	SNPRINTF(buf + num, sizeof(buf) - num, ", wsErr=%d(0x%X)", wsErr, wsErr);
#endif
	OBJ_MERR(M_NETWORK, "%s", buf);
}

#if (ENABLE_CRYPTO)
/*
bool
socket_stream::set_encrypt_key(char* pKey, int len)
{
    if ( !m_pCryptor || pKey == NULL )
        return false;

    m_pCryptor->SetKey(pKey, len);
    return true;
}*/

bool
socket_stream::is_crypted()
{
    return (m_pCryptor && m_pCryptor->IsEnabled() && m_bEncrypt);
};

/*
void
socket_stream::set_encrypt(bool enable, DWORD mode)
{
    if( !m_pCryptor )
        return;
    m_pCryptor->SetEnable(enable);
    m_bEncrypt = enable;
    m_pCryptor->SetMode(mode);
}*/

void
socket_stream::set_stamp(StZetaStamp * stamp)
{
    // OBJ_TRC("set_stamp: m_pCryptor: 0x%08X, stamp: 0x%08X", m_pCryptor, stamp);

    if( !m_pCryptor )
        return;
    
    m_pCryptor->m_ZetaTotalEncrypt = stamp;
}

StZetaStamp *
socket_stream::get_stamp()
{
    
    return (m_pCryptor ? m_pCryptor->m_ZetaTotalEncrypt : 0 );

}

#if (ENABLE_PERF_LOG)
int
socket_stream::send(const void * pBuf, unsigned int iSize, bool bSmallPkt, bool bCrypted, int iOffset)
{
    int ret = 0;
    PERFMON({\
        ret = log_send(pBuf, iSize, bSmallPkt, bCrypted, iOffset);
    }, "[%s]StreamWrite (size:%d)", m_szSocketName, iSize);
    return ret;
}

int
socket_stream::log_send(const void * pBuf, unsigned int iSize, bool bSmallPkt, bool bCrypted, int iOffset)
#else
int
socket_stream::send(const void * pBuf, unsigned int iSize, bool bSmallPkt, bool bCrypted, int iOffset)
#endif
{
    char* p = (char*)pBuf;
    char* pSend = NULL;
    int iSend = iSize;
    int remain = 0;

    OBJ_DBG("[%s]send packet with size: %d, small packet: %s, encryption: %s, offset %d",
            m_szSocketName,
            iSize,
            bSmallPkt ? "on" : "off",
            bCrypted ? "on" : "off",
            iOffset);

    bool bCrypt = (is_crypted() && bCrypted);

    if ( bCrypt ) 
    {
        if ( (iSize - iOffset) <= 0 ) // send plantext part only
            return send_packet(pBuf, iSize);

        iSend = m_pCryptor->CountDataSize(iSize - iOffset) + iOffset;
        pSend = m_pCryptor->AllocateBuffer(iSend);

        if ( !pSend )
        {
            OBJ_ERR("[%s]CryptoSend: AllocateBufferFail", m_szSocketName);
            iSend = 0;
            goto end;
        }

        memcpy(pSend, pBuf, iOffset);

#if (_DEBUG)
        if (iOffset)
        {
            char h[1024] = "\0";
            for(int i = 0; i < iOffset; i++)
                sprintf(h, "%s %02x", h, (unsigned char)pSend[i]);
            OBJ_DBG("Send packet with offset data: %s", h);
        }
#endif

        PERFMON({\
        iSend = m_pCryptor->Encrypt((char*) pBuf + iOffset, iSize - iOffset, pSend + iOffset, iSend - iOffset);
        }, "[%s]StreamWrite_Encrypt (size:%d)", m_szSocketName, iSize);

        if ( iSend <= 0 ) {
            OBJ_ERR("[%s]CryptoSend: EncryptFail (orig size:%d, enc size: %d)", m_szSocketName, iSize, iSend);
            goto end;
        }
        p = pSend;
        iSend += iOffset;
    }

    PERFMON({ \
        iSend = send_packet(p, iSend, bSmallPkt ? DEFAULT_SMALL_PKT_SIZE : 0 );
    }, "[%s]StreamWrite_Send_Packet (size:%d)", m_szSocketName, iSend);

#if 0
    PERFMON({ \
    // Normal mode
    if (!bSmallPkt || iSend <= DEFAULT_SMALL_PKT_SIZE) {
        iSend = send_packet(p, iSend);
    } else {
        // Small package mode
        remain = iSend;
        do {
            int len = remain > DEFAULT_SMALL_PKT_SIZE ? DEFAULT_SMALL_PKT_SIZE : remain;
            remain -= send_packet(p, len);
            p += len;
        } while ( remain > 0);

        if ( remain < 0 )
            OBJ_ERR("[%s]SmallPkg: SendDataOverflow (%d)", m_szSocketName, remain);
    }
    }, "[%s]StreamWrite_Send_Raw_Packet (size:%d)", m_szSocketName, iSend);
#endif

end:
    if ( pSend != NULL )
        m_pCryptor->DestroyBuffer(pSend);
    /*
     * Sgar: Note here we return iSend which is actually the iSize + IV, m_pCryptor->CountDataSize(iSize)
     */
    return iSend;
}

#if (ENABLE_PERF_LOG)
int
socket_stream::recv(void * pBuf, unsigned int iSize, bool bCrypted)
{
    int ret = 0;
    PERFMON({\
        ret = log_recv(pBuf, iSize, bCrypted);
    }, "[%s]StreamRead (size:%d)", m_szSocketName, iSize);
    return ret;
}

int
socket_stream::log_recv(void * pBuf, unsigned int iSize, bool bCrypted)
#else
int
socket_stream::recv(void * pBuf, unsigned int iSize, bool bCrypted)
#endif
{
    char *pRecv = (char*) pBuf;
    int iRecv = iSize;
    int actual = 0;

    OBJ_DBG("[%s]recv packet with size: %d, encryption: %s",
            m_szSocketName,
            iSize,
            bCrypted ? "on" : "off" );

    bool bCrypt = (is_crypted() && bCrypted);

    if ( bCrypt )
    {
        iRecv = m_pCryptor->CountDataSize(iSize);
        pRecv = m_pCryptor->AllocateBuffer(iRecv);
    }

    PERFMON({ \
    actual = recv_packet(pRecv, iRecv);
    }, "[%s]StreamRead_Recieve_Raw_Packet (size:%d)", m_szSocketName, iSize);

    if ( actual != iRecv )
    {
        OBJ_ERR("[%s]CryptRecv: SizeMismatch (expect: %d, actually: %d byte)", m_szSocketName, iRecv, actual);
        actual = 0;
		if ( bCrypt )
		{
			m_pCryptor->DestroyBuffer(pRecv);
		}
        goto exit;
    }

    if ( bCrypt )
    {
        PERFMON( { \
        actual = m_pCryptor->Decrypt(pRecv, iRecv, (char*)pBuf, iSize);
        }, "[%s]StreamRead_Decrypt (size:%d)", m_szSocketName, iSize );
        m_pCryptor->DestroyBuffer(pRecv);

        if ( actual != iSize ) {
            OBJ_ERR("[%s]CryptRecv: DecryptFail", m_szSocketName);
            actual = 0;
        }
    }

exit:
    return actual;
}

void
socket_stream::set_socket_name(const char* name)
{
    int len = strlen(name);
    len = len > 128 ? 128 : len;
    memset(m_szSocketName, 0, 128);
    strncpy(m_szSocketName, name, len);

#if (SZETA_ENABLE)
    m_ZetaSockPktSend->StampSetName(name);
#endif
}

DWORD
socket_stream::MonitorStreamRead(VOID* buf, DWORD size, DWORD channel)
{

    // OBJ_TRC("m_pCryptor: 0x%08X, m_nChannels: 0x%08X, channel: 0x%08x", m_pCryptor, m_nChannels, channel);

	if ( !m_pCryptor || !m_pCryptor->IsEnabled() || (m_nChannels & channel) == 0)
		return recv_packet(buf, size);

	OBJ_DBG("MonitorStreamRead channel: 0x%08x with encryption", (m_nChannels & channel));
	int recv = m_pCryptor->CountDataSize(size);
	char *tmp = m_pCryptor->AllocateBuffer(recv);

	int total = recv_packet(tmp, recv);
	if ( total != recv ) {
		OBJ_ERR("recv_packet failed");
		recv = 0;
		goto end;
	}

	recv = m_pCryptor->Decrypt(tmp, recv, (char *)buf, size);
end:
	m_pCryptor->DestroyBuffer(tmp);
	return recv;
}

DWORD
socket_stream::MonitorStreamWrite(VOID* buf, DWORD size, DWORD channel, int offset)
{
	if ( !m_pCryptor || !m_pCryptor->IsEnabled() || (m_nChannels & channel) == 0)
		return send_packet(buf, size);

	OBJ_TRC("MSW: channel: 0x%08x with encryption", (m_nChannels & channel));    
	OBJ_DBG("MonitorStreamWrite channel: 0x%08x with encryption", (m_nChannels & channel));

	int sent = m_pCryptor->CountDataSize(size - offset) + offset;
	char *tmp = m_pCryptor->AllocateBuffer(sent);
	memcpy(tmp, buf, offset);
	sent = m_pCryptor->Encrypt((char *)buf + offset, size - offset, tmp + offset, sent - offset);
	sent = send_packet(tmp, sent + offset);
	m_pCryptor->DestroyBuffer(tmp);
	return size;
}

void
socket_stream::SetCryptor(STCryptor* pCryptor)
{
	m_pCryptor = pCryptor;
}

#endif /* ENABLE_CRYPTO */
